Bahasa Indonesia

Buka kekuatan penggabungan namespace TypeScript! Panduan ini menjelajahi pola deklarasi modul canggih untuk modularitas, ekstensibilitas, dan kode yang lebih bersih, dengan contoh praktis untuk developer TypeScript global.

Penggabungan Namespace TypeScript: Pola Deklarasi Modul Tingkat Lanjut

TypeScript menawarkan fitur-fitur canggih untuk menyusun dan mengorganisasi kode Anda. Salah satu fitur tersebut adalah penggabungan namespace, yang memungkinkan Anda mendefinisikan beberapa namespace dengan nama yang sama, dan TypeScript akan secara otomatis menggabungkan deklarasi mereka menjadi satu namespace tunggal. Kemampuan ini sangat berguna untuk memperluas pustaka yang sudah ada, membuat aplikasi modular, dan mengelola definisi tipe yang kompleks. Panduan ini akan membahas secara mendalam pola-pola canggih untuk memanfaatkan penggabungan namespace, memberdayakan Anda untuk menulis kode TypeScript yang lebih bersih dan lebih mudah dipelihara.

Memahami Namespace dan Modul

Sebelum mendalami penggabungan namespace, sangat penting untuk memahami konsep dasar namespace dan modul di TypeScript. Meskipun keduanya menyediakan mekanisme untuk organisasi kode, mereka berbeda secara signifikan dalam lingkup dan penggunaannya.

Namespace (Modul Internal)

Namespace adalah konstruksi khusus TypeScript untuk mengelompokkan kode terkait. Mereka pada dasarnya membuat wadah bernama untuk fungsi, kelas, antarmuka, dan variabel Anda. Namespace terutama digunakan untuk organisasi kode internal dalam satu proyek TypeScript. Namun, dengan maraknya modul ES, namespace umumnya kurang disukai untuk proyek-proyek baru kecuali Anda memerlukan kompatibilitas dengan basis kode yang lebih lama atau skenario augmentasi global tertentu.

Contoh:


namespace Geometry {
  export interface Shape {
    getArea(): number;
  }

  export class Circle implements Shape {
    constructor(public radius: number) {}

    getArea(): number {
      return Math.PI * this.radius * this.radius;
    }
  }
}

const myCircle = new Geometry.Circle(5);
console.log(myCircle.getArea()); // Output: 78.53981633974483

Modul (Modul Eksternal)

Modul, di sisi lain, adalah cara standar untuk mengorganisasi kode, yang didefinisikan oleh modul ES (modul ECMAScript) dan CommonJS. Modul memiliki lingkupnya sendiri dan secara eksplisit mengimpor dan mengekspor nilai, menjadikannya ideal untuk membuat komponen dan pustaka yang dapat digunakan kembali. Modul ES adalah standar dalam pengembangan JavaScript dan TypeScript modern.

Contoh:


// circle.ts
export interface Shape {
  getArea(): number;
}

export class Circle implements Shape {
  constructor(public radius: number) {}

  getArea(): number {
    return Math.PI * this.radius * this.radius;
  }
}

// app.ts
import { Circle } from './circle';

const myCircle = new Circle(5);
console.log(myCircle.getArea());

Kekuatan Penggabungan Namespace

Penggabungan namespace memungkinkan Anda untuk mendefinisikan beberapa blok kode dengan nama namespace yang sama. TypeScript secara cerdas menggabungkan deklarasi ini menjadi satu namespace tunggal pada saat kompilasi. Kemampuan ini sangat berharga untuk:

Pola Deklarasi Modul Tingkat Lanjut dengan Penggabungan Namespace

Mari kita jelajahi beberapa pola canggih untuk memanfaatkan penggabungan namespace dalam proyek TypeScript Anda.

1. Memperluas Pustaka yang Ada dengan Deklarasi Ambien

Salah satu kasus penggunaan paling umum untuk penggabungan namespace adalah untuk memperluas pustaka JavaScript yang ada dengan definisi tipe TypeScript. Bayangkan Anda menggunakan pustaka JavaScript bernama `my-library` yang tidak memiliki dukungan TypeScript resmi. Anda dapat membuat file deklarasi ambien (misalnya, `my-library.d.ts`) untuk mendefinisikan tipe untuk pustaka ini.

Contoh:


// my-library.d.ts
declare namespace MyLibrary {
  interface Options {
    apiKey: string;
    timeout?: number;
  }

  function initialize(options: Options): void;
  function fetchData(endpoint: string): Promise;
}

Sekarang, Anda dapat menggunakan namespace `MyLibrary` dalam kode TypeScript Anda dengan keamanan tipe:


// app.ts
MyLibrary.initialize({
  apiKey: 'YOUR_API_KEY',
  timeout: 5000,
});

MyLibrary.fetchData('/api/data')
  .then(data => {
    console.log(data);
  });

Jika Anda perlu menambahkan lebih banyak fungsionalitas ke definisi tipe `MyLibrary` nanti, Anda cukup membuat file `my-library.d.ts` lain atau menambahkannya ke yang sudah ada:


// my-library.d.ts

declare namespace MyLibrary {
  interface Options {
    apiKey: string;
    timeout?: number;
  }

  function initialize(options: Options): void;
  function fetchData(endpoint: string): Promise;

  // Add a new function to the MyLibrary namespace
  function processData(data: any): any;
}

TypeScript akan secara otomatis menggabungkan deklarasi-deklarasi ini, memungkinkan Anda untuk menggunakan fungsi `processData` yang baru.

2. Menambah Objek Global

Terkadang, Anda mungkin ingin menambahkan properti atau metode ke objek global yang ada seperti `String`, `Number`, atau `Array`. Penggabungan namespace memungkinkan Anda melakukan ini dengan aman dan dengan pemeriksaan tipe.

Contoh:


// string.extensions.d.ts
declare global {
  interface String {
    reverse(): string;
  }
}

String.prototype.reverse = function() {
  return this.split('').reverse().join('');
};

console.log('hello'.reverse()); // Output: olleh

Dalam contoh ini, kita menambahkan metode `reverse` ke prototipe `String`. Sintaks `declare global` memberi tahu TypeScript bahwa kita sedang memodifikasi objek global. Penting untuk dicatat bahwa meskipun ini mungkin, menambah objek global terkadang dapat menyebabkan konflik dengan pustaka lain atau standar JavaScript di masa depan. Gunakan teknik ini dengan bijaksana.

Pertimbangan Internasionalisasi: Saat menambah objek global, terutama dengan metode yang memanipulasi string atau angka, perhatikan internasionalisasi. Fungsi `reverse` di atas bekerja untuk string ASCII dasar, tetapi mungkin tidak cocok untuk bahasa dengan set karakter yang kompleks atau arah penulisan dari kanan ke kiri. Pertimbangkan untuk menggunakan pustaka seperti `Intl` untuk manipulasi string yang sadar lokal.

3. Memodulasi Namespace Besar

Saat bekerja dengan namespace yang besar dan kompleks, akan bermanfaat untuk memecahnya menjadi file-file yang lebih kecil dan lebih mudah dikelola. Penggabungan namespace membuat ini mudah dicapai.

Contoh:


// geometry.ts
namespace Geometry {
  export interface Shape {
    getArea(): number;
  }
}

// circle.ts
namespace Geometry {
  export class Circle implements Shape {
    constructor(public radius: number) {}

    getArea(): number {
      return Math.PI * this.radius * this.radius;
    }
  }
}

// rectangle.ts
namespace Geometry {
  export class Rectangle implements Shape {
    constructor(public width: number, public height: number) {}

    getArea(): number {
      return this.width * this.height;
    }
  }
}

// app.ts
/// 
/// 
/// 

const myCircle = new Geometry.Circle(5);
const myRectangle = new Geometry.Rectangle(10, 5);

console.log(myCircle.getArea()); // Output: 78.53981633974483
console.log(myRectangle.getArea()); // Output: 50

Dalam contoh ini, kita telah membagi namespace `Geometry` menjadi tiga file: `geometry.ts`, `circle.ts`, dan `rectangle.ts`. Setiap file berkontribusi pada namespace `Geometry`, dan TypeScript menggabungkannya. Perhatikan penggunaan direktif `/// `. Meskipun ini berfungsi, ini adalah pendekatan yang lebih lama, dan menggunakan modul ES umumnya lebih disukai dalam proyek TypeScript modern, bahkan saat menggunakan namespace.

Pendekatan Modul Modern (Lebih Disukai):


// geometry.ts
export namespace Geometry {
  export interface Shape {
    getArea(): number;
  }
}

// circle.ts
import { Geometry } from './geometry';

export namespace Geometry {
  export class Circle implements Shape {
    constructor(public radius: number) {}

    getArea(): number {
      return Math.PI * this.radius * this.radius;
    }
  }
}

// rectangle.ts
import { Geometry } from './geometry';

export namespace Geometry {
  export class Rectangle implements Shape {
    constructor(public width: number, public height: number) {}

    getArea(): number {
      return this.width * this.height;
    }
  }
}

// app.ts
import { Geometry } from './geometry';
const myCircle = new Geometry.Circle(5);
const myRectangle = new Geometry.Rectangle(10, 5);

console.log(myCircle.getArea());
console.log(myRectangle.getArea());

Pendekatan ini menggunakan modul ES bersama dengan namespace, memberikan modularitas yang lebih baik dan kompatibilitas dengan perkakas JavaScript modern.

4. Menggunakan Penggabungan Namespace dengan Augmentasi Antarmuka

Penggabungan namespace sering digabungkan dengan augmentasi antarmuka untuk memperluas kemampuan tipe yang ada. Ini memungkinkan Anda untuk menambahkan properti atau metode baru ke antarmuka yang didefinisikan di pustaka atau modul lain.

Contoh:


// user.ts
interface User {
  id: number;
  name: string;
}

// user.extensions.ts
namespace User {
  export interface User {
    email: string;
  }
}

// app.ts
import { User } from './user'; // Assuming user.ts exports the User interface
import './user.extensions'; // Import for side-effect: augment the User interface

const myUser: User = {
  id: 123,
  name: 'John Doe',
  email: 'john.doe@example.com',
};

console.log(myUser.name);
console.log(myUser.email);

Dalam contoh ini, kita menambahkan properti `email` ke antarmuka `User` menggunakan penggabungan namespace dan augmentasi antarmuka. File `user.extensions.ts` menambah antarmuka `User`. Perhatikan impor `./user.extensions` di `app.ts`. Impor ini semata-mata untuk efek sampingnya dalam menambah antarmuka `User`. Tanpa impor ini, augmentasi tidak akan berlaku.

Praktik Terbaik untuk Penggabungan Namespace

Meskipun penggabungan namespace adalah fitur yang kuat, penting untuk menggunakannya dengan bijaksana dan mengikuti praktik terbaik untuk menghindari potensi masalah:

Pertimbangan Global

Saat mengembangkan aplikasi untuk audiens global, perhatikan pertimbangan berikut saat menggunakan penggabungan namespace:

Contoh lokalisasi dengan `Intl` (API Internasionalisasi):


// number.extensions.d.ts
declare global {
  interface Number {
    toCurrencyString(locale: string, currency: string): string;
  }
}

Number.prototype.toCurrencyString = function(locale: string, currency: string) {
  return new Intl.NumberFormat(locale, {
    style: 'currency',
    currency: currency,
  }).format(this);
};

const price = 1234.56;

console.log(price.toCurrencyString('en-US', 'USD')); // Output: $1,234.56
console.log(price.toCurrencyString('de-DE', 'EUR')); // Output: 1.234,56 €
console.log(price.toCurrencyString('ja-JP', 'JPY')); // Output: ¥1,235

Contoh ini menunjukkan cara menambahkan metode `toCurrencyString` ke prototipe `Number` menggunakan API `Intl.NumberFormat`, yang memungkinkan Anda memformat angka sesuai dengan lokal dan mata uang yang berbeda.

Kesimpulan

Penggabungan namespace TypeScript adalah alat yang kuat untuk memperluas pustaka, memodulasi kode, dan mengelola definisi tipe yang kompleks. Dengan memahami pola-pola canggih dan praktik terbaik yang diuraikan dalam panduan ini, Anda dapat memanfaatkan penggabungan namespace untuk menulis kode TypeScript yang lebih bersih, lebih mudah dipelihara, dan lebih skalabel. Namun, ingatlah bahwa modul ES seringkali merupakan pendekatan yang lebih disukai untuk proyek-proyek baru, dan penggabungan namespace harus digunakan secara strategis dan bijaksana. Selalu pertimbangkan implikasi global dari kode Anda, terutama ketika berhadapan dengan lokalisasi, pengodean karakter, dan konvensi budaya, untuk memastikan bahwa aplikasi Anda dapat diakses dan digunakan oleh pengguna di seluruh dunia.

Penggabungan Namespace TypeScript: Pola Deklarasi Modul Tingkat Lanjut | MLOG